package com.guonl.utils;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.util.CollectionUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SqlUtil {

    /**
     * 获取sql语句
     */
    public static String getSql(MappedStatement mappedStatement, Object parameterObject) {

        Configuration configuration = mappedStatement.getConfiguration();
        BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);

        // 获取参数
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

        // sql语句中多个空格都用一个空格代替
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) {
            // 获取类型处理器注册器，类型处理器的功能是进行java类型和数据库类型的转换
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            // 如果根据parameterObject.getClass(）可以找到对应的类型，则替换
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?",
                        Matcher.quoteReplacement(formatValue(parameterObject)));
            } else {
                // MetaObject主要是封装了originalObject对象，提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?",
                                Matcher.quoteReplacement(formatValue(obj)));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        // 该分支是动态sql
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?",
                                Matcher.quoteReplacement(formatValue(obj)));
                    } else {
                        sql = sql.replaceFirst("\\?", "缺失");
                    }
                }
            }
        }
        return sql;
    }

    /**
     * 获取表名
     */
    public static String getTableName(String sql) {

        // 正则表达式匹配 INSERT 语句中的表名
        Pattern pattern = Pattern.compile("\\bINTO\\s+(?:\\w+\\.)?(\\w+)\\s+SET");

        // 忽略大小写
        Matcher matcher = pattern.matcher(sql);

        // 提取匹配的表名
        if (matcher.find()) {
            return matcher.group(1);
        }

        // 正则表达式匹配 UPDATE 语句中的表名
        pattern = Pattern.compile("\\bUPDATE\\s+(?:\\w+\\.)?(\\w+)\\s+SET");
        matcher = pattern.matcher(sql);

        // 提取匹配的表名
        if (matcher.find()) {
            return matcher.group(1);
        }

        // 正则表达式匹配 DELETE 语句中的表名
        pattern = Pattern.compile("\\bDELETE\\s+(?:\\w+\\.)?(\\w+)\\s+SET");
        matcher = pattern.matcher(sql);

        // 提取匹配的表名
        if (matcher.find()) {
            return matcher.group(1);
        }

        return null;
    }

    /**
     * 将参数值格式化为 SQL 字符串格式
     */
    private static String formatValue(Object value) {

        // 字符串类型，在值两端加单引号
        if(value instanceof String) {
            String data = ((String) value).trim();
            // 如果字符串包含"'", 替换为 "\\\\'"
            if(data.contains("'")) {
                data = data.replace("'", "\\'");
            }
            return "'" + data + "'";
        }
        // 日期时间类型，使用 SimpleDateFormat 格式化
        else if (value instanceof Date) {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return "'" + dateFormat.format((Date) value) + "'";
        }
        // 布尔类型，直接返回字符串格式
        else if (value instanceof Boolean) {
            return ((boolean) value) ? "true" : "false";
        }
        // 其他类型，直接使用 toString() 方法
        else {
            return value.toString();
        }
    }
}

